﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Data;
using CoreEx.Common.Extensions;
using CoreEx.DynamicInvoker;
using LinFu.IoC.Interceptors;
using LinFu.AOP.Interfaces;
using LinFu.IoC.Configuration;
using LinFu.IoC;



namespace CoreEx.Database.Client.Implementation
{
    [Implements(typeof(IInvokeWrapper<IDbConnection>),LifecycleType.OncePerRequest)]
    public class ConnectionInvokeWrapper : InvokeWrapperBase<IDbConnection>
    {
        
        private string _name;
        private IDbTransaction _dbTransaction;
        private string _providerName; 


        public ConnectionInvokeWrapper(IDbConnection actualTarget) : base(actualTarget)
        {
        }

        public override void BeforeInvoke(IInvocationInfo info)
        {
            if (info.TargetMethod.DeclaringType == typeof(IDisposable))
            {
                _dbTransaction = null;
            }

            if (info.TargetMethod.DeclaringType == typeof(IDbConnection))
            {
                if (info.TargetMethod.Name == "BeginTransaction")
                    ServiceContainer.GetServices<IProfiler<IDbTransaction>>().ForEach(p => p.BeforeInvoke(info));
                
                if (info.TargetMethod.Name == "Close")
                {
                    _dbTransaction = null;
                }            
            }
            base.BeforeInvoke(info);
        }

        public override void AfterInvoke(IInvocationInfo info, object returnValue)
        {
            if (info.TargetMethod.DeclaringType == typeof(IDbConnection))
                if (info.TargetMethod.Name == "BeginTransaction")
                    ServiceContainer.GetServices<IProfiler<IDbTransaction>>().ForEach(p => p.AfterInvoke(info,returnValue));
            base.AfterInvoke(info, returnValue);
        }


        

        public override object DoInvoke(IInvocationInfo info)
        {
            if (info.TargetMethod.DeclaringType == typeof(IDbConnection))
            {                                
                if (info.TargetMethod.Name == "CreateCommand")                
                    return CreateCommand(info);

                if (info.TargetMethod.Name == "BeginTransaction")
                    return BeginTransaction(info);                                
            }
            if (info.TargetMethod.DeclaringType == typeof(IDbConnectionInfo))
            {
                if (info.TargetMethod.Name == "set_Name")
                {
                    _name = (string) info.Arguments[0];
                    return null;
                }
                if (info.TargetMethod.Name == "get_Name")
                    return _name;

                if (info.TargetMethod.Name == "set_ProviderName")
                {
                    _providerName = (string)info.Arguments[0];
                    return null;
                }
                if (info.TargetMethod.Name == "get_ProviderName")
                    return _providerName;

                if (info.TargetMethod.Name == "set_DbTransaction")
                {
                    _dbTransaction = (IDbTransaction)info.Arguments[0];
                    return null;
                }
                if (info.TargetMethod.Name == "get_DbTransaction")
                    return _dbTransaction;
            }

            return base.DoInvoke(info);
        }

        private object CreateCommand(IInvocationInfo info)
        {                        
            var providerFactory = ServiceContainer.GetService<DbProviderFactory>(_name);
            IDbCommand command = providerFactory.CreateCommand();            
            IDbCommand proxyCommand = ServiceContainer.GetService<IProxyFactory<IDbCommand>>().CreateProxy(command);
            proxyCommand.Connection = (IDbConnection) info.Target;
            proxyCommand.Transaction = _dbTransaction;                        
            return proxyCommand;
        }

        private object BeginTransaction(IInvocationInfo info)
        {
            var transaction = (IDbTransaction) info.TargetMethod.DynamicInvoke(ActualTarget, info.Arguments);
            _dbTransaction = ServiceContainer.GetService<IProxyFactory<IDbTransaction>>().CreateProxy(transaction);
            return _dbTransaction;
        }
    }
}
